package tr.edu.metu.is550.isell.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tr.edu.metu.is550.isell.domain.*;
import tr.edu.metu.is550.isell.repository.MessageRepository;
import tr.edu.metu.is550.isell.repository.PropertyRepository;
import tr.edu.metu.is550.isell.repository.UserRepository;
import tr.edu.metu.is550.isell.rest.message.SearchPropertyMessage;
import tr.edu.metu.is550.isell.rest.message.SearchPropertyResultMessage;

import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

@Service
public class PropertyServiceImpl implements PropertyService {

    @Autowired
    private MessageRepository messageRepository;

    @Autowired
    private PropertyRepository propertyRepository;

    @Autowired
    private UserRepository userRepository;

    @PersistenceUnit
    private EntityManagerFactory entityManagerFactory;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Property addProperty(Property property) {
        return propertyRepository.save(property);
    }

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void updateProperty(String updatingUserName, Property newProperty) {

        Property propertyToBeUpdated = propertyRepository.findById(newProperty.getId());

        if (propertyToBeUpdated == null)
            throw new NotFoundException("user", String.valueOf(newProperty.getId()));

        if (!propertyToBeUpdated.getAdvertisedBy().getUserCredentials().getLoginName().equals(updatingUserName)) {
            throw new AuthorizationException();
        }

        propertyToBeUpdated.setNumberOfRooms(newProperty.getNumberOfRooms());
        propertyToBeUpdated.setPropertyType(newProperty.getPropertyType());
        propertyToBeUpdated.setAddress(newProperty.getAddress());
        propertyToBeUpdated.setAdtype(newProperty.getAdtype());
        propertyToBeUpdated.setDetails(newProperty.getDetails());
        propertyToBeUpdated.setLocation(newProperty.getLocation());
        propertyToBeUpdated.setPrice(newProperty.getPrice());
        propertyToBeUpdated.setTitle(newProperty.getTitle());

        propertyRepository.save(propertyToBeUpdated);

    }

    @Override
    public void deleteProperty(Property property) {
        Property propertyToBeDeleted = propertyRepository.findById(property.getId());

        if (propertyToBeDeleted == null)
            throw new NotFoundException("property", String.valueOf(property.getId()));

        propertyRepository.delete(propertyToBeDeleted.getId());
    }

    @Override
    public List<SearchPropertyResultMessage> searchProperty(SearchPropertyMessage searchPropertyMessage) {

        StringBuilder queryStr = new StringBuilder("SELECT id, title FROM properties WHERE acos(sin(radians(");
        queryStr.append(searchPropertyMessage.getLatitude());
        queryStr.append(")) * sin(radians(latitude)) + cos(radians(");
        queryStr.append(searchPropertyMessage.getLatitude());
        queryStr.append(")) * cos(radians(latitude)) * cos(radians(longitude) - (radians(");
        queryStr.append(searchPropertyMessage.getLogitude());
        queryStr.append(")))) * 6371 <=");
        queryStr.append(searchPropertyMessage.getRadius());

        if (searchPropertyMessage.getMinPrice() != null) {
            queryStr.append(" and price >=").append(searchPropertyMessage.getMinPrice());
        }

        if (searchPropertyMessage.getMaxPrice() != null) {
            queryStr.append(" and price <=").append(searchPropertyMessage.getMaxPrice());
        }

        if (searchPropertyMessage.getMinNumOfRooms() != null) {
            queryStr.append(" and numberOfRooms >=").append(searchPropertyMessage.getMinNumOfRooms());
        }

        if (searchPropertyMessage.getMaxNumOfRooms() != null) {
            queryStr.append(" and numberOfRooms <=").append(searchPropertyMessage.getMaxNumOfRooms());
        }

        if (searchPropertyMessage.getAdType() != null) {
            queryStr.append(" and adType =").append(searchPropertyMessage.getAdType());
        }

        if (searchPropertyMessage.getPropertyType() != null) {
            queryStr.append(" and propertyType =").append(searchPropertyMessage.getPropertyType());
        }


        List<SearchPropertyResultMessage> result = jdbcTemplate.query(queryStr.toString(), new RowMapper<SearchPropertyResultMessage>() {
            @Override
            public SearchPropertyResultMessage mapRow(ResultSet rs, int rowNum) throws SQLException {
                return new SearchPropertyResultMessage(rs.getLong(1), rs.getString(2));
            }
        });

        return result;
    }

    @Override
    public Property getProperty(Long propertyId) {

        Property propertyFound = propertyRepository.findById(propertyId);

        if (propertyFound == null)
            throw new NotFoundException("property", String.valueOf(propertyId));

        return propertyFound;
    }

    @Override
    public Message addComment(Long id, String username, String comment) {
        Property property = propertyRepository.findById(id);

        if (property == null)
            throw new NotFoundException("property", String.valueOf(id));

        User byUserCredentialsLoginName = userRepository.findByUserCredentialsLoginName(username);

        if (byUserCredentialsLoginName == null)
            throw new NotFoundException("user", String.valueOf(username));

        Message message = new Message();
        message.setMessageOwner(byUserCredentialsLoginName);
        message.setMessageText(comment);
        message.setMessageVisibility(MessageVisibility.PUBLIC);
        message.setProperty(property);

        return messageRepository.save(message);

    }


}
